package com.chaosj.webmagic.handler;

import cn.hutool.core.text.StrFormatter;
import com.chaosj.tools.StringFormatUtils;
import com.chaosj.webmagic.md.StorageApi;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.parser.Tag;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @className: WriterMarkdownTools
 * @description:
 * @author: caoyangjie
 * @date: 2022/3/10
 **/
@Slf4j
public class WriterMarkdownTools {
    private String baseDir;
    private String articleName;
    private String url;

    public WriterMarkdownTools(String baseDir, String articleName, String url) {
        this.baseDir = baseDir;
        this.articleName = articleName;
        this.url = url;
    }

    public List<String> executeComment(Element element, List<String> lines, StringBuffer appendBuffer){
        List<Node> children = element.childNodes();
        for ( Node child : children ) {
            if( child instanceof Element ){
                Element childElement = (Element) child;
                Tag tag = childElement.tag();
                if( tag.getName().equals("article") || tag.getName().equalsIgnoreCase("section") ) {
                    childElement.children().forEach(mdTag->{
                        try{
                            extractedTagElement(lines, mdTag, appendBuffer);
                        } catch(Exception e){
                            log.error("writerMarkdowwnTool -> 51: "+e.getMessage());
                            System.err.println("writerMarkdownTool -> 51: "+e.getMessage());
                            throw e;
                        } finally {
                        }
                    });
                }else{
                    executeComment(childElement, lines, appendBuffer);
                }
            } else if( child instanceof TextNode){
                lines.add(((TextNode) child).text());
            }
        }
        return lines;
    }

    private void extractedTagElement(List<String> lines, Element mdTag, StringBuffer appendBuffer) {
        String tagName = mdTag.tagName();
        if( tagName.matches("^h[0-9]+$") ){
            h(mdTag, lines, appendBuffer);
            return;
        }
        if( tagName.equalsIgnoreCase("p") ){
            lines.add("");
            p(mdTag, lines, appendBuffer);
            lines.add("");
            return;
        }
        if( tagName.equalsIgnoreCase("li") ){
            li(mdTag, lines, appendBuffer);
            return;
        }
        if( tagName.equalsIgnoreCase("ol") ){
            ol(mdTag, lines, appendBuffer);
            return;
        }
        if( tagName.equalsIgnoreCase("a") ){
            a(mdTag, lines, appendBuffer);
            return;
        }
        if( tagName.equalsIgnoreCase("code") ){
            code(mdTag, lines, appendBuffer);
            return;
        }
        if( tagName.equalsIgnoreCase("img") ){
            img(mdTag, lines, appendBuffer);
            return;
        }
        if( tagName.equalsIgnoreCase("div") ){
            executeSummary( mdTag, lines, 0 );
            return;
        }
        if( tagName.equalsIgnoreCase("tr") ){
            tr( mdTag, lines, appendBuffer );
            return;
        }
        defaultTag( mdTag, lines, appendBuffer );
    }

    private void tr(Element tr, List<String> lines, StringBuffer appendBuffer) {
        StringBuffer buffer = new StringBuffer("|");
        StringBuffer segmentBuffer = new StringBuffer("|");
        boolean isHead = false;
        for( Node node : tr.childNodes() ){
            if( node instanceof Element ){
                String nodeValue = ((Element) node).text();
                if( StringUtils.isBlank(nodeValue) ){
                    nodeValue = "    ";
                }
                if( ((Element) node).tagName().equalsIgnoreCase("th") ){
                    buffer.append(nodeValue).append("|");
                    segmentBuffer.append(Stream.generate(()->"").limit(StringFormatUtils.getWordCount(nodeValue)+1).collect(Collectors.joining("-"))).append("|");
                    isHead = true;
                }
                if(((Element) node).tagName().equalsIgnoreCase("td")){
                    buffer.append((nodeValue)).append("|");
                }
            }
        }
        if( isHead ){
            lines.add("");
        }
        lines.add(buffer.toString());
        if( isHead ){
            lines.add(segmentBuffer.toString());
        }
    }

    private void img(Element img, List<String> lines, StringBuffer appendBuffer) {
        String imgTemplate = "![{}](./images/{})";
        String imgSrc = getImgAttr(img, "data-original", "src");
        String title = getImgAttr(img, "data-alt", "alt");
        List<String> imgNames = Splitter.on("/").omitEmptyStrings().trimResults().splitToList(imgSrc);
        String imgName = imgNames.get(imgNames.size()-1);
        if( StorageApi.storageImage(baseDir+ File.separator+articleName, "/images/"+imgName, imgSrc) ){
            lines.add(StrFormatter.format(imgTemplate, title, imgName));
        } else {
            lines.add(StrFormatter.format("![{}]({})", title, imgSrc));
        }
    }

    private String getImgAttr(Element img, String... attrs) {
        String[] imgAttr = new String[1];
        Stream.of(attrs).forEach(attr->{
            if( StringUtils.isEmpty(imgAttr[0]) && img.hasAttr(attr) ){
                imgAttr[0] = img.attr(attr);
            }
        });
        return imgAttr[0];
    }

    private void ol(Element ol, List<String> lines, StringBuffer appendBuffer) {
        lines.add("```");
        defaultTag(ol, lines, appendBuffer);
        lines.add("```");
    }

    private void code(Element code, List<String> lines, StringBuffer appendBuffer) {
        for( Node element : code.childNodes() ){
            if( element instanceof TextNode ){
                Optional.ofNullable(appendBuffer).orElse(appendBuffer=new StringBuffer()).append(((TextNode) element).text());
            }else if( element instanceof Element ){
                if( ((Element) element).tagName().equalsIgnoreCase("span") ){
                    Optional.ofNullable(appendBuffer).orElse(appendBuffer=new StringBuffer("")).append(span((Element) element, new StringBuffer("")));
                }
            }
        }
        lines.add(appendBuffer.toString());
    }

    private String span(Element span, StringBuffer appendBuffer) {
        span.childNodes().forEach(node->{
            if( node instanceof TextNode ){
                appendBuffer.append(((TextNode) node).text());
            }else if( node instanceof Element ){
                if( ((Element) node).tagName().equalsIgnoreCase("span") ){
                    span((Element) node, appendBuffer);
                }
            }
        });
        return appendBuffer.toString();
    }

    private void defaultTag(Element element, List<String> lines, StringBuffer appendBuffer) {
        element.childNodes().forEach(node->{
            if( node instanceof Element ){
                extractedTagElement(lines, (Element) node, appendBuffer);
            }else if( node instanceof TextNode ){
                if(!StringUtils.isBlank(((TextNode) node).text())){
                    lines.add( (appendBuffer==null?new StringBuffer(""):appendBuffer).append(((TextNode) node).text()).toString() );
                }
            }
        });
    }

    private void a(Element mdTag, List<String> lines, StringBuffer appendBuffer) {
        String aTemplate = "[{}]({})";
        String title = mdTag.text();
        String link = mdTag.attr("href");
        lines.add(Optional.ofNullable(appendBuffer).orElse(new StringBuffer("")).append(StrFormatter.format(aTemplate, title, extractHref(link))).toString());
    }

    private void p(Element pTag, List<String> lines, StringBuffer appendBuffer){
        appendBuffer = appendBuffer==null? new StringBuffer("") : appendBuffer;
        for( Node node : pTag.childNodes() ){
            if( node instanceof TextNode ){
                appendBuffer.append(((TextNode) node).text());
            } else if ( node instanceof Element ){
                List<String> plines = new ArrayList<>();
                extractedTagElement(plines, (Element) node, new StringBuffer(""));
                appendBuffer.append(Joiner.on("\n").join(plines));
            }
        }
        lines.add(appendBuffer.toString());
    }

    private void li(Element liTag, List<String> lines, StringBuffer appendBuffer) {
        if( liTag.hasAttr("class") ){
            appendBuffer = new StringBuffer("");
//            int level = Integer.valueOf(liTag.attr("class").substring(1));
//            if( level > 1 ){
//                appendBuffer = new StringBuffer().append(Stream.generate(()->"").limit(level).collect(Collectors.joining("  ")));
//            }
        }
        if( appendBuffer == null ){
            appendBuffer = new StringBuffer("* ");
        }
        for( Node node : liTag.childNodes() ){
            if( node instanceof TextNode ){
                appendBuffer.append(((TextNode) node).text());
            } else if ( node instanceof Element ){
                List<String> liLines = new ArrayList<>();
                extractedTagElement(liLines, (Element) node, new StringBuffer(""));
                if( ((Element) node).tagName().equalsIgnoreCase("ul") ){
                    appendBuffer.append(Joiner.on("\n").join(liLines));
                }else{
                    appendBuffer.append(Joiner.on("").join(liLines));
                }
            }
        }
        lines.add(appendBuffer.toString());
    }

    private void h(Element elementH, List<String> lines, StringBuffer appendBuffer){
        int level = Integer.valueOf(elementH.tagName().substring(1));
        String head = Stream.generate(()->"").limit(level+1).collect(Collectors.joining("#"));
        String content = elementH.text();
        lines.add(StrFormatter.format("{} {}",head, content));
    }

    private List<String> executeSummary(Element element, List<String> lines, int spaceCount){
        List<Node> children = element.childNodes();
        for (Node child : children) {
            if (child instanceof Element) {
                Element childElement = (Element) child;
                Tag tag = childElement.tag();
                String tagName = tag.getName();
                if( tagName.equals("li") ){
                    childElement.children().forEach(liChild->{
                        if( liChild.tagName().equals("a") ){
                            String link = liChild.attr("href");
                            String title = liChild.attr("title");
                            if( StringUtils.isEmpty(title) ){
                                title = liChild.text();
                            }
                            String head = Stream.generate(()->"").limit((spaceCount*4)+1).collect(Collectors.joining(" "));
                            List<String> fileName = Splitter.on("/").omitEmptyStrings().trimResults().splitToList(link);
                            lines.add(StrFormatter.format("{}* [{}]({})", head, title, link.startsWith("#") ? link : ("./"+fileName.get(fileName.size()-1))));
                        } else if( liChild.tagName().equals("ul") ){
                            executeSummary(liChild, lines, spaceCount+1);
                        }
                    });
                }else{
                    executeSummary( childElement, lines, spaceCount);
                }
            }
        }
        return lines;
    }

    private String extractHref(String href){
        if( StringUtils.isBlank(href) || href.startsWith("#") ){
            return href;
        }
        // 路径为 http 开头,并且不是本域名地址, 则直接链接到 第三方路径
        if( href.startsWith("http") && !href.startsWith(url) ){
            return href;
        }
        List<String> splitters = Splitter.on("/").splitToList(href);
        return splitters.get(splitters.size()-1);
    }
}
